package itsm.isperp.workflow.cmd;

import itsm.isperp.framework.core.context.ContextHolder;
import itsm.isperp.framework.utils.HibernateUUIDGenerator;
import itsm.isperp.module.entity.bpm.BpmActionHistory;
import itsm.isperp.module.service.bpm.BpmActionHistoryService;
import itsm.isperp.workflow.graph.ActivitiHistoryGraphBuilder;
import itsm.isperp.workflow.graph.Edge;
import itsm.isperp.workflow.graph.Graph;
import itsm.isperp.workflow.graph.Node;
import itsm.isperp.workflow.utils.BpmConst;
import itsm.isperp.workflow.utils.BpmScriptUtils;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.activiti.engine.impl.HistoricActivityInstanceQueryImpl;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.HistoricActivityInstanceEntity;
import org.activiti.engine.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;

/**
 * 退回任务.
 */
public class RollbackTaskCmd implements Command<Integer> {
	private static Logger logger = LoggerFactory
			.getLogger(RollbackTaskCmd.class);
	private String taskId;
	private String processId;
	private String comment;
	private String actionUserId;

	public RollbackTaskCmd(String taskId, String processId, String comment,
			String actionUserId) {
		this.taskId = taskId;
		this.processId = processId;
		this.comment = comment;
		this.actionUserId = actionUserId;

	}

	/**
	 * 退回流程.
	 * 
	 * @return 0-退回成功 1-流程结束 2-下一结点已经通过,不能退回
	 */
	@Override
	public Integer execute(CommandContext commandContext) {

		String historyTaskId = new FindNearestUserTaskCmd(this.taskId)
				.execute(Context.getCommandContext());

		if (historyTaskId == null) {
			logger.info("cannot rollback {}", taskId);
			return 2;
		}

		HistoricTaskInstanceEntity historicTaskInstanceEntity = Context
				.getCommandContext().getHistoricTaskInstanceEntityManager()
				.findHistoricTaskInstanceById(historyTaskId);
		HistoricActivityInstanceEntity historicActivityInstanceEntity = getHistoricActivityInstanceEntity(historyTaskId);
		// Node node = null;
		// try {
		// Graph graph = new ActivitiHistoryGraphBuilder(
		// historicTaskInstanceEntity.getProcessInstanceId()).build();
		//
		// node = graph.findById(historicActivityInstanceEntity.getId());
		//
		// if (!checkCouldRollback(node)) {
		// logger.info("cannot rollback {}", taskId);
		//
		// return 2;
		// }
		//
		// } catch (Exception ex) {
		//
		// }

		if (this.isSameBranch(historicTaskInstanceEntity)) {
			// 如果退回的目标节点的executionId与当前task的executionId一样，说明是同一个分支
			// 只删除当前分支的task
			this.deleteActiveTask();
		} else {
			// 否则认为是从分支跳回主干
			// 删除所有活动中的task
			this.deleteActiveTasks(historicTaskInstanceEntity
					.getProcessInstanceId());

			// 获得期望退回的节点后面的所有节点历史
			List<String> historyNodeIds = new ArrayList<String>();
			// try {
			// collectNodes(node, historyNodeIds);
			// } catch (Exception ex) {
			//
			// }

			this.deleteHistoryActivities(historyNodeIds);
		}

		// 恢复期望退回的任务和历史
		ExecutionEntity e = this.processHistoryTask(historicTaskInstanceEntity,
				historicActivityInstanceEntity);

		logger.info("activiti is rollback {}",
				historicTaskInstanceEntity.getName());

		// 记录
		BpmActionHistoryService bpmActionHistoryService = ContextHolder
				.getSpringBean(BpmActionHistoryService.class);

		BpmActionHistory entity = new BpmActionHistory();
		entity.setId(HibernateUUIDGenerator.generate());
		entity.setActionName("退回");
		entity.setActionTime(new Timestamp(new Date().getTime()));
		entity.setActionUserId(this.actionUserId);
		entity.setComment(this.comment);
		entity.setProcessId(this.processId);
		entity.setTaskId(this.taskId);
		entity.setRelationId(e.getBusinessKey());

		bpmActionHistoryService.saveOrUpdate(entity);

		return 0;
	}

	public boolean isSameBranch(
			HistoricTaskInstanceEntity historicTaskInstanceEntity) {
		TaskEntity taskEntity = Context.getCommandContext()
				.getTaskEntityManager().findTaskById(taskId);

		return taskEntity.getExecutionId().equals(
				historicTaskInstanceEntity.getExecutionId());
	}

	public HistoricActivityInstanceEntity getHistoricActivityInstanceEntity(
			String historyTaskId) {
		logger.info("historyTaskId : {}", historyTaskId);

		JdbcTemplate jdbcTemplate = ContextHolder
				.getSpringBean(JdbcTemplate.class);
		String historicActivityInstanceId = jdbcTemplate.queryForObject(
				"select id_ from ACT_HI_ACTINST where task_id_=?",
				String.class, historyTaskId);
		logger.info("historicActivityInstanceId : {}",
				historicActivityInstanceId);

		HistoricActivityInstanceQueryImpl historicActivityInstanceQueryImpl = new HistoricActivityInstanceQueryImpl();
		historicActivityInstanceQueryImpl
				.activityInstanceId(historicActivityInstanceId);

		HistoricActivityInstanceEntity historicActivityInstanceEntity = (HistoricActivityInstanceEntity) Context
				.getCommandContext()
				.getHistoricActivityInstanceEntityManager()
				.findHistoricActivityInstancesByQueryCriteria(
						historicActivityInstanceQueryImpl, new Page(0, 1))
				.get(0);

		return historicActivityInstanceEntity;
	}

	public boolean checkCouldRollback(Node node) {
		// TODO: 如果是catchEvent，也应该可以退回，到时候再说
		for (Edge edge : node.getEdges()) {
			Node dest = edge.getDest();
			String type = dest.getType();

			if ("userTask".equals(type)) {
				if (!dest.isActive()) {
					logger.info("cannot rollback, " + type + "("
							+ dest.getName() + ") is complete.");

					return false;
				}
			} else if (type.endsWith("Gateway")) {
				if (!checkCouldRollback(dest)) {
					return false;
				}
			} else {
				logger.info("cannot rollback, " + type + "(" + dest.getName()
						+ ") is complete.");

				return false;
			}
		}

		return true;
	}

	public void deleteActiveTasks(String processInstanceId) {
		Context.getCommandContext().getTaskEntityManager()
				.deleteTasksByProcessInstanceId(processInstanceId, "退回", false);

		JdbcTemplate jdbcTemplate = ContextHolder
				.getSpringBean(JdbcTemplate.class);
		List<Map<String, Object>> list = jdbcTemplate
				.queryForList(
						"select * from ACT_HI_ACTINST where proc_inst_id_=? and end_time_ is null",
						processInstanceId);
		Date now = new Date();

		for (Map<String, Object> map : list) {
			Date startTime = (Date) map.get("start_time_");
			long duration = now.getTime() - startTime.getTime();
			jdbcTemplate
					.update("update ACT_HI_ACTINST set end_time_=?,duration_=? where id_=?",
							now, duration, map.get("id_"));
		}
	}

	public void collectNodes(Node node, List<String> historyNodeIds) {

		for (Edge edge : node.getEdges()) {
			logger.info("edge : {}", edge.getName());

			Node dest = edge.getDest();
			historyNodeIds.add(dest.getId());
			collectNodes(dest, historyNodeIds);
		}
	}

	public void deleteHistoryActivities(List<String> historyNodeIds) {
		/*
		 * JdbcTemplate jdbcTemplate = ApplicationContextHelper
		 * .getBean(JdbcTemplate.class); logger.info("historyNodeIds : {}",
		 * historyNodeIds);
		 * 
		 * for (String id : historyNodeIds) {
		 * jdbcTemplate.update("delete from ACT_HI_ACTINST where id_=?", id); }
		 */
	}

	public ExecutionEntity processHistoryTask(
			HistoricTaskInstanceEntity historicTaskInstanceEntity,
			HistoricActivityInstanceEntity historicActivityInstanceEntity) {
		/*
		 * historicTaskInstanceEntity.setEndTime(null);
		 * historicTaskInstanceEntity.setDurationInMillis(null);
		 * historicActivityInstanceEntity.setEndTime(null);
		 * historicActivityInstanceEntity.setDurationInMillis(null);
		 */

		// 创建新任务
		TaskEntity task = TaskEntity.create(new Date());
		task.setProcessDefinitionId(historicTaskInstanceEntity
				.getProcessDefinitionId());
		task.setId(Context.getProcessEngineConfiguration().getIdGenerator()
				.getNextId());
		task.setAssigneeWithoutCascade(historicTaskInstanceEntity.getAssignee());
		task.setParentTaskIdWithoutCascade(historicTaskInstanceEntity
				.getParentTaskId());
		task.setNameWithoutCascade(historicTaskInstanceEntity.getName());
		task.setTaskDefinitionKey(historicTaskInstanceEntity
				.getTaskDefinitionKey());
		task.setExecutionId(historicTaskInstanceEntity.getExecutionId());
		task.setPriority(historicTaskInstanceEntity.getPriority());
		task.setProcessInstanceId(historicTaskInstanceEntity
				.getProcessInstanceId());
		task.setExecutionId(historicTaskInstanceEntity.getExecutionId());
		task.setDescriptionWithoutCascade(historicTaskInstanceEntity
				.getDescription());

		Context.getCommandContext().getTaskEntityManager().insert(task);

		// 把流程指向任务对应的节点
		ExecutionEntity executionEntity = Context.getCommandContext()
				.getExecutionEntityManager()
				.findExecutionById(historicTaskInstanceEntity.getExecutionId());
		executionEntity
				.setActivity(getActivity(historicActivityInstanceEntity));
		executionEntity.getBusinessKey();
		// 创建HistoricActivityInstance
		Context.getCommandContext().getHistoryManager()
				.recordActivityStart(executionEntity);

		// 创建HistoricTaskInstance
		Context.getCommandContext().getHistoryManager()
				.recordTaskCreated(task, executionEntity);
		Context.getCommandContext().getHistoryManager().recordTaskId(task);

		try {
			BpmScriptUtils.exeEventScript(task, executionEntity,
					BpmConst.StartScript,
					historicTaskInstanceEntity.getProcessDefinitionId(),
					historicActivityInstanceEntity.getActivityId());

		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return executionEntity;
	}

	public ActivityImpl getActivity(
			HistoricActivityInstanceEntity historicActivityInstanceEntity) {
		ProcessDefinitionEntity processDefinitionEntity = new FindProcessDefinitionEntityCmd(
				historicActivityInstanceEntity.getProcessDefinitionId())
				.execute(Context.getCommandContext());

		return processDefinitionEntity
				.findActivity(historicActivityInstanceEntity.getActivityId());
	}

	public void deleteActiveTask() {
		TaskEntity taskEntity = Context.getCommandContext()
				.getTaskEntityManager().findTaskById(taskId);
		Context.getCommandContext().getTaskEntityManager()
				.deleteTask(taskEntity, "回退", false);

		JdbcTemplate jdbcTemplate = ContextHolder
				.getSpringBean(JdbcTemplate.class);
		List<Map<String, Object>> list = jdbcTemplate
				.queryForList(
						"select * from ACT_HI_ACTINST where task_id_=? and end_time_ is null",
						taskId);
		Date now = new Date();

		for (Map<String, Object> map : list) {
			Date startTime = (Date) map.get("start_time_");
			long duration = now.getTime() - startTime.getTime();
			jdbcTemplate
					.update("update ACT_HI_ACTINST set end_time_=?,duration_=? where id_=?",
							now, duration, map.get("id_"));
		}
	}

}
